package edu.uiowa.smallworld;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.util.Log;

public class Utils {
	public static String IP = "http://128.255.25.155/SmallWorld/";
	
	public static boolean HttpTimeout = false;
	
	public static String userResponse = "";
	
	private static String TAG = "utils activity";
	private static String VALID = "valid";
	
	/**
	 * Executes a PHP script to validate a register/login request from a user
	 * 
	 * @param service
	 * @param nameValuePairs
	 * @return true is valid, else false
	 */
	public static boolean executeUserService(String service, ArrayList<NameValuePair> nameValuePairs) {
		StringBuilder stringBuilder = executeService(service, nameValuePairs);
		if(TextUtils.isEmpty(stringBuilder)) return false;
		
		Utils.userResponse = parseUserResponse(stringBuilder);
		if(!validateUserResponse(Utils.userResponse)) {
			Log.d(Utils.TAG, service + " returned invalid response");
			return false;
		}
		
		Log.d(Utils.TAG, service + " returned valid response");
		return true;
	}
	
	/**
	 * Executes a PHP script to get connection data for the given alias
	 * 
	 * @param service
	 * @param nameValuePairs
	 * @return true if success, else false
	 */
	public static boolean getConnectionData(String service, ArrayList<NameValuePair> nameValuePairs) {
		StringBuilder stringBuilder = executeService(service, nameValuePairs);
		if(TextUtils.isEmpty(stringBuilder)) return false;
		
		ViewConnectionsActivity.connectionsList = parseConnectionResponse(stringBuilder);
		if(!validateConnectionsResponse(ViewConnectionsActivity.connectionsList)) {
			Log.d(Utils.TAG, service + " returned invalid response");
			return false;
		}
		
		Log.d(Utils.TAG, service + " returned valid response");
		return true;
	}
	
	/**
	 * Executes a PHP request and returns a StringBuilder object in response. The string can be
	 * parsed into a JSON object
	 * 
	 * @param service
	 * @param nameValuePairs
	 * @return StringBuilder if success else null
	 */
	private static StringBuilder executeService(String service, ArrayList<NameValuePair> nameValuePairs) {
		HttpParams httpParams;
		HttpClient httpClient;
		HttpPost httpPost;
		HttpResponse httpResponse;
		HttpEntity httpEntity;
		
		InputStream inputStream;
		StringBuilder stringBuilder;
		
		try {
			Log.d(Utils.TAG, Utils.IP + service);
			
			Utils.HttpTimeout = false;
			
			// set connection and read timeout
			httpParams = new BasicHttpParams();
			//HttpConnectionParams.setConnectionTimeout(httpParams, 10000);
			//HttpConnectionParams.setSoTimeout(httpParams, 10000);
			
			httpClient = new DefaultHttpClient(httpParams);
			
			// create request parameters
			String params = "?";
			for(int i = 0; i < nameValuePairs.size(); i++)
			{
				String name = nameValuePairs.get(i).getName();
				String value = nameValuePairs.get(i).getValue();
				
				params += name + "=" + value + "&";
			}
			
			Log.d(Utils.TAG, Utils.IP + service + params);
			
			// execute request via httpPost
			httpPost = new HttpPost(Utils.IP + service + params);
			httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
			httpResponse = httpClient.execute(httpPost);
			httpEntity = httpResponse.getEntity();
			
			inputStream = httpEntity.getContent(); // get response content
			if(inputStream == null) return null;
			
			Log.d(Utils.TAG, "HTTP connection successful");
			
			stringBuilder = readInputStream(inputStream);
		}
		catch(Exception e) {
			Utils.HttpTimeout = true;
			
			Log.e(Utils.TAG, "Error in executing " + service + ": " + e.toString());
			return null;
		}
		
		return stringBuilder;
	}
	
	/**
	 * Reads input stream from HTTP request and returns a StringBuilder object
	 * 
	 * @param inputStream
	 * @return StringBuilder if success, else null
	 */
	private static StringBuilder readInputStream(InputStream inputStream) {
		StringBuilder stringBuilder = null;
		
		try {
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
			stringBuilder = new StringBuilder();
			
			String inputLine = null;
			while((inputLine = bufferedReader.readLine()) != null) {
				stringBuilder.append(inputLine); // append response to StringBuilder
			}
			bufferedReader.close();
			
			Log.d(Utils.TAG, stringBuilder.toString());
		}
		catch(Exception e) {
			String message = "Error reading HTTP response: " + e.toString();
			Log.e(Utils.TAG, message);
		}
		
		return stringBuilder; // null if error
	}
	
	/**
	 * Parses the JSON response returned from register/login requests
	 * 
	 * @param stringBuilder
	 * @return String - response
	 */
	private static String parseUserResponse(StringBuilder stringBuilder) {
		String response = null;
		
		try {
			JSONObject jsonObj = new JSONObject(stringBuilder.toString());
			Boolean valid = jsonObj.getBoolean("valid");
			
			if(!valid) response = jsonObj.getString("error"); // return invalid message
			else response = Utils.VALID; // return valid message
			
			Log.d(Utils.TAG, response);
			
			return response;
		} catch (JSONException e) {
			String message = "Error parsing user JSON response: " + e.toString();
			Log.e(Utils.TAG, message);
			
			return null;
		}
	}
	
	/**
	 * Validates response from register/login requests. Compares response string
	 * to expected valid response
	 * 
	 * @param response
	 * @return true if valid, else false
	 */
	private static boolean validateUserResponse(String response) {
		return TextUtils.equals(response, Utils.VALID);
	}
	
	/**
	 * Gets connection data from JSON response and creates a list of HashMap objects
	 * <id, alias, location, datetime>
	 * 
	 * @param stringBuilder
	 * @return List<HashMap<String, String>> if success else null
	 */
	private static List<HashMap<String, String>> parseConnectionResponse(StringBuilder stringBuilder) {
		List<HashMap<String, String>> connectionData = null;
		HashMap<String, String> map;
		
		Log.d(Utils.TAG, "parsing connection response");
		
		try {
			JSONObject jsonObj = new JSONObject(stringBuilder.toString());
			
			Boolean valid = (jsonObj.length() > 0);
			if(!valid) return null;
			
			connectionData = new ArrayList<HashMap<String, String>>();
			
			Log.d(Utils.TAG, "length: " + jsonObj.length());
			
			Iterator<String> keys = jsonObj.keys();
			while(keys.hasNext()) {
				String key = keys.next();
				
				JSONObject connection = jsonObj.getJSONObject(key);
				Log.d(Utils.TAG, key + ": " + connection.toString());
				
				map = new HashMap<String, String>();
				map.put("id", key);
				map.put("alias", connection.getString("alias"));
				map.put("location", connection.getString("location"));
				map.put("datetime", connection.getString("time_date"));
				connectionData.add(map);
			}
			
			return connectionData;
		} catch(JSONException e) {
			String message = "Error parsing connections JSON response: " + e.toString();
			Log.e(Utils.TAG, message);
			
			return null;
		}
	}
	
	/**
	 * Validates connection data returned from PHP request.
	 * 
	 * @param connectionData
	 * @return true if not null else false
	 */
	private static boolean validateConnectionsResponse(List<HashMap<String, String>> connectionData) {
		return connectionData != null;
	}
	
	/**
	 * Indicates if there is a current user logged in
	 * 
	 * @param packageContext context
	 * @return true - current user logged exists, false - no current user
	 */
	public static boolean isLoggedIn() {
		return LoginActivity.currentUser != null;
	}
}
